#pragma once

#include <iostream>
#include <functional>
using namespace std;

namespace gp
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{
			;
		}

		auto_ptr(auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return &_ptr;
		}

		~auto_ptr()
		{
			delete _ptr;
		}
	private:
		T* _ptr;
	};


	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{
			;
		}

		unique_ptr(unique_ptr<T>& up) = delete;
		unique_ptr<T>& operator=(unique_ptr<T>& up) = delete;

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		~unique_ptr()
		{
			delete _ptr;
		}
	private:
		T* _ptr;
	};


	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr=nullptr)
			:_ptr(ptr),
			_count(new int(1))
		{
			;
		}

		template <class D>
		shared_ptr(T* ptr,const D& del)
			:_ptr(ptr),
			_count(new int(1)),
			_del(del)
		{
			;
		}

		shared_ptr(shared_ptr<T>& sp)
			:_ptr(sp._ptr),
			_count(sp._count)
		{
			++(*_count);
		}

		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			if (_ptr == sp._ptr)
				return *this;

			if (--(*(_count)) == 0)
			{
				_del(_ptr);
				delete _count;
			}

			_ptr = sp._ptr;
			_count = sp._count;
			++(*(_count));

			return *this;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		~shared_ptr()
		{
			if (--(*(_count)) == 0)
			{
				_del(_ptr);
				delete _count;
			}
		}

	//private:
		T* _ptr;
		int* _count;
		function<void(T*)> _del = [](T* ptr) ->void {delete ptr; };
	};

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{
			;
		}

		weak_ptr(shared_ptr<T>& sp)
			:_ptr(sp._ptr)
		{
			;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		weak_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			_ptr = sp._ptr;
			return *this;
		}

	private:
		T* _ptr;
	};
}


